package cn.st4rlight.util.value;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * 一些数值安全工具
 *
 * @author st4rlight <st4rlight@163.com>
 * Created on 2024-05-05
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SafeUtil {

    /* ------------------------------- 集合类型 ------------------------------- */
    public static <K, V> Map<K, V> of(Map<K, V> source) {
        return Optional.ofNullable(source).orElseGet(Maps::newHashMap);
    }

    public static <T> List<T> of(List<T> source) {
        return Optional.ofNullable(source).orElseGet(Lists::newArrayList);
    }

    public static <T> Set<T> of(Set<T> source) {
        return Optional.ofNullable(source).orElseGet(Sets::newHashSet);
    }

    public static <T> T[] of(T[] source) {
        return Optional.ofNullable(source).orElseGet(() -> (T[]) new Object[0]);
    }

    public static String of(String source) {
        return Optional.ofNullable(source).orElse(StringUtils.EMPTY);
    }

    public static String of(String source, String defaultValue) {
        return Optional.ofNullable(source).orElse(defaultValue);
    }

    public static Integer of(Integer source) {
        return Optional.ofNullable(source).orElse(0);
    }

    public static Integer of(Integer source, Integer defaultValue) {
        return Optional.ofNullable(source).orElse(defaultValue);
    }

    public static Long of(Long source) {
        return Optional.ofNullable(source).orElse(0L);
    }

    public static Long of(Long source, Long defaultValue) {
        return Optional.ofNullable(source).orElse(defaultValue);
    }

    public static Float of(Float source) {
        return Optional.ofNullable(source).orElse(0F);
    }

    public static Float of(Float source, Float defaultValue) {
        return Optional.ofNullable(source).orElse(defaultValue);
    }

    public static Double of(Double source) {
        return Optional.ofNullable(source).orElse(0D);
    }

    public static Double of(Double source, Double defaultValue) {
        return Optional.ofNullable(source).orElse(defaultValue);
    }

    public static Boolean of(Boolean source) {
        return Optional.ofNullable(source).orElse(false);
    }

    public static Boolean of(Boolean source, Boolean defaultValue) {
        return Optional.ofNullable(source).orElse(defaultValue);
    }

    public static BigDecimal of(BigDecimal source) {
        return Optional.ofNullable(source).orElse(BigDecimal.ZERO);
    }

    public static <T> Optional<T> first(Collection<T> collection) {
        return CollectionUtils.isEmpty(collection)
               ? Optional.empty()
               : Optional.ofNullable(IterableUtils.first(collection));
    }

    public static <T> Optional<T> first(T[] array) {
        return ArrayUtils.isEmpty(array)
               ? Optional.empty()
               : Optional.ofNullable(ArrayUtils.get(array, 0));
    }

    public static <T> Optional<T> last(Collection<T> collection) {
        return CollectionUtils.isEmpty(collection)
               ? Optional.empty()
               : Optional.ofNullable(IterableUtils.get(collection, collection.size() - 1));
    }

    public static <T> Optional<T> last(T[] array) {
        return ArrayUtils.isEmpty(array)
               ? Optional.empty()
               : Optional.ofNullable(ArrayUtils.get(array, array.length - 1));
    }

    public static <T> Optional<T> get(List<T> collection, int index) {
        if (CollectionUtils.isEmpty(collection) || collection.size() < index + 1) {
            return Optional.empty();
        }
        return Optional.ofNullable(collection.get(index));
    }

    public static <T> Optional<T> get(T[] array, int index) {
        if (ArrayUtils.isEmpty(array) || array.length < index + 1) {
            return Optional.empty();
        }
        return Optional.ofNullable(array[index]);
    }

    public static <T> Optional<T> getCatch(Supplier<T> supplier) {
        try {
            return Optional.ofNullable(supplier.get());
        } catch (Exception ex) {
            return Optional.empty();
        }
    }

    public static <T> T getCatch(Supplier<T> supplier, T defaultValue) {
        return SafeUtil.getCatch(supplier).orElse(defaultValue);
    }
}
